package server

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/sirupsen/logrus"
	"github.com/wi-cuckoo/eastlake/core"
	"github.com/wi-cuckoo/eastlake/schedule"
)

// Server to serve rpc api
type Server struct {
	token string
}

// NewServer create a server
func NewServer(token string) *Server {
	return &Server{
		token: token,
	}
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if s.token == "" || r.Header.Get("X-Drone-Token") != s.token {
		w.WriteHeader(401) // not authorized
		return
	}
	logrus.WithField("path", r.URL.Path).Debugf("got request from %s", r.RemoteAddr)
	switch r.URL.Path {
	case "/rpc/v1/write":
		s.handleWrite(w, r)
	case "/rpc/v1/request":
		s.handleRequest(w, r)
	case "/rpc/v1/accept":
		s.handleAccept(w, r)
	case "/rpc/v1/netrc":
		s.handleNetrc(w, r)
	case "/rpc/v1/details":
		s.handleDetails(w, r)
	case "/rpc/v1/before":
		s.handleBefore(w, r)
	case "/rpc/v1/after":
		s.handleAfter(w, r)
	case "/rpc/v1/beforeAll":
		s.handleBeforeAll(w, r)
	case "/rpc/v1/afterAll":
		s.handleAfterAll(w, r)
	case "/rpc/v1/watch":
		s.handleWatch(w, r)
	case "/rpc/v1/upload":
		s.handleUpload(w, r)
	default:
		w.WriteHeader(404)
	}
}

func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
	in := &requestRequest{}
	err := json.NewDecoder(r.Body).Decode(in)
	if err != nil {
		writeBadRequest(w, err)
		return
	}
	sid := schedule.Dequeue()
	if sid < 0 {
		logrus.Infof("no stage to schedule after timeout")
		return
	}
	logrus.Infof("schedule stage %d to runner", sid)
	stage := TestStage
	json.NewEncoder(w).Encode(stage)
}

func (s *Server) handleAccept(w http.ResponseWriter, r *http.Request) {
	in := &acceptRequest{}
	err := json.NewDecoder(r.Body).Decode(in)
	if err != nil {
		writeBadRequest(w, err)
		return
	}
	out := core.Stage{}
	json.NewEncoder(w).Encode(out)
}

func (s *Server) handleDetails(w http.ResponseWriter, r *http.Request) {
	in := struct {
		Stage int64
	}{}
	err := json.NewDecoder(r.Body).Decode(&in)
	if err != nil {
		writeBadRequest(w, err)
		return
	}
	out := &buildContextToken{
		Secret:  TestSecret,
		Context: TestContext,
	}
	json.NewEncoder(w).Encode(out)
}

func (s *Server) handleNetrc(w http.ResponseWriter, r *http.Request) {
	in := &netrcRequest{}
	err := json.NewDecoder(r.Body).Decode(in)
	if err != nil {
		writeBadRequest(w, err)
		return
	}
	netrc := TestNetrc
	json.NewEncoder(w).Encode(netrc)
}

func (s *Server) handleBefore(w http.ResponseWriter, r *http.Request) {
	in := struct {
		Step *core.Step
	}{}
	err := json.NewDecoder(r.Body).Decode(&in)
	if err != nil {
		writeBadRequest(w, err)
		return
	}
	fmt.Printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> start step: %s <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", in.Step.Name)
	json.NewEncoder(w).Encode(in.Step)
}
func (s *Server) handleAfter(w http.ResponseWriter, r *http.Request) {
	in := struct {
		Step *core.Step
	}{}
	err := json.NewDecoder(r.Body).Decode(&in)
	if err != nil {
		writeBadRequest(w, err)
		return
	}
	json.NewEncoder(w).Encode(in.Step)
}

func (s *Server) handleBeforeAll(w http.ResponseWriter, r *http.Request) {
	in := struct {
		Stage *core.Stage
	}{}
	err := json.NewDecoder(r.Body).Decode(&in)
	if err != nil {
		writeBadRequest(w, err)
		return
	}
	json.NewEncoder(w).Encode(in.Stage)
}

func (s *Server) handleAfterAll(w http.ResponseWriter, r *http.Request) {
	in := struct {
		Stage *core.Stage
	}{}
	err := json.NewDecoder(r.Body).Decode(&in)
	if err != nil {
		writeBadRequest(w, err)
		return
	}
	fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> All Done !!!! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
	json.NewEncoder(w).Encode(in.Stage)
}

func (s *Server) handleWrite(w http.ResponseWriter, r *http.Request) {
	in := struct {
		Step int64 `json:"step"`
		Line struct {
			Number    int    `json:"pos"`
			Message   string `json:"out"`
			Timestamp int64  `json:"time"`
		} `json:"line"`
	}{}

	err := json.NewDecoder(r.Body).Decode(&in)
	if err != nil {
		writeBadRequest(w, err)
		return
	}
	fmt.Println(in.Line.Message)
	w.WriteHeader(http.StatusNoContent)
}

func (s *Server) handleUpload(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusNoContent)
}

func (s *Server) handleWatch(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	ctx, cancel := context.WithTimeout(ctx, time.Second*30)
	defer cancel()

	in := struct {
		Build int64
	}{}
	err := json.NewDecoder(r.Body).Decode(&in)
	if err != nil {
		writeBadRequest(w, err)
		return
	}
	json.NewEncoder(w).Encode(struct{ Done bool }{false})
}

func writeBadRequest(w http.ResponseWriter, err error) {
	w.WriteHeader(500) // should retry
	io.WriteString(w, err.Error())
}
